package com.googlecode.bindyj.property.watcher;

import com.googlecode.bindyj.function.Func;
import com.googlecode.bindyj.property.accessor.PropertyAccessor;
import com.googlecode.bindyj.property.listener.GenericPropertyListener;
import com.googlecode.bindyj.property.listener.NativePropertyListener;
import com.googlecode.bindyj.property.listener.PropertyEvent;
import com.googlecode.bindyj.property.listener.PropertyListenerAdapter;

public class Watcher {

  public static Watcher[] collect(Func<?> func, Object... arguments) {
    return null;
  }

  public static void watcherCreated(Watcher watcher) {

  }

  private final PropertyAccessor accessor;

  private final NativePropertyListener listener;

  private final WatcherProvider childWatcherProvider;

  private Object host;

  private Func<?> handler;

  private Watcher childWatcher;

  public Watcher(Object host, PropertyAccessor accessor, PropertyListenerAdapter listenerAdapter,
      WatcherProvider childWatcherProvider) {
    this.accessor = accessor;
    this.listener = listenerAdapter.adaptListener(new GenericPropertyListener() {
      public void handleChange(PropertyEvent event) {
        Watcher.this.handleChange(event);
      }
    });
    this.childWatcherProvider = childWatcherProvider;
    this.host = host;

    updateChildWatcher();
  }

  protected void handleChange(PropertyEvent event) {
    updateChildWatcher();

    if (handler != null) {
      handler.call(event);
    }
  }

  public void unwatch() {
    setHost(null);
  }

  public Object getHost() {
    return host;
  }

  public void setHost(Object newHost) {
    Object oldHost = this.host;

    if (oldHost != null && handler != null) {
      listener.removeFrom(oldHost);
    }

    this.host = newHost;
    updateChildWatcher();

    if (newHost != null && handler != null) {
      listener.addTo(newHost);
    }
  }

  private void updateChildWatcher() {
    if (childWatcher != null) {
      childWatcher.unwatch();
      childWatcher = null;
    }

    Object hostValue = getHostValue();
    if (hostValue != null) {
      childWatcher = childWatcherProvider.watcher(hostValue);
      childWatcher.setHandler(handler);
    }
  }

  private Object getHostValue() {
    return host == null
        ? null
        : accessor.get(host);
  }

  public Func<?> getHandler() {
    return handler;
  }

  public void setHandler(Func<?> newHandler) {
    Func<?> oldHandler = this.handler;

    if (oldHandler != newHandler) {
      if (host != null && newHandler == null) {
        listener.removeFrom(host);
      }

      this.handler = newHandler;
      if (childWatcher != null) {
        childWatcher.setHandler(newHandler);
      }

      if (host != null && oldHandler == null) {
        listener.addTo(host);
      }
    }
  }

}
